#pragma once

#include <string>
#include <set>
#include <functional>
#include "Singleton.h"
#include "Md5.h"
#include "json.h"
#include "def.h"
#include "streamdata.h"
#include "util.h"
#include "context.h"

template <int server, int cmd_req, int cmd_resp, typename Req, typename Resp>
class RpcProxy {
public:
    bool Init()
    {
        Ctx.msg_router.BindMsg(cmd_resp, std::bind(&RpcProxy::OnHandleCb, this, std::placeholders::_1));
        return true;
    }
public:
    inline void ListenOnHandleCb(ListenSence sence, std::function<void(Resp&&)> listen)
    {
        m_listen[sence] = listen;
    }
private:
    std::map<ListenSence, std::function<void(Resp&&)>> m_listen;

public:
    bool Handle(Req&& req, ListenSence sence)
    {
        log_debug("enter,sence:%d,req:%s", sence, req.ShortDebugString().c_str());

        StreamData stream_data;
        StreamDataWriter write(stream_data);
        write.Write<ListenSence>(sence);
        write.WriteInt64(CTools::GetUnixTimeMs());
        write.WriteString(std::move(req.trans()));

        req.set_trans(std::move(stream_data.Data()));

        return 0 == Ctx.host->MtSendtoSrv(cmd_req, server, &req, 0);
    }

    void OnHandleCb(MsgData&& data)
    {
        log_debug("enter,stype:%hu,svid:%u,uuid:%u", data.svtype, data.svid, data.uuid);
        do
        {
            Resp resp;
            if (!resp.ParseFromArray(data.data.c_str(), data.data.size()))
            {
                log_error("parser pb failed, cmd:%d,stype:%hu,svid:%u,uuid:%d", cmd_resp, data.svtype, data.svid, data.uuid);
                break;
            }

            StreamData stream_data(std::move(*resp.mutable_trans()));
            StreamDataReader reader(stream_data);
            ListenSence sence = reader.Read<ListenSence>();
            std::uint64_t req_time = reader.ReadInt64();
            resp.set_trans(std::move(reader.ReadString()));

            std::uint64_t resp_time = CTools::GetUnixTimeMs();
            std::uint64_t cost_time = resp_time-req_time;

            if (cost_time > 50000)  log_warning("uid:%u gen token cost %lu us", resp.uid(), cost_time);
            else log_debug("uid:%u gen token cost %lu us", resp.uid(), cost_time);

            auto it = m_listen.find(sence);
            if (it == m_listen.end())
            {
                log_info("sence:%d not listen, stype:%hu,svid:%u,uuid:%d", sence, data.svtype, data.svid, data.uuid);
                break;
            }
            try
            {
                
                it->second(std::move(resp));
            }
            catch(const std::exception& e)
            {
                log_error("catch exception:%s, sence:%d, stype:%hu,svid:%u,uuid:%u,msg:%s", e.what(), sence, data.svtype, data.svid, data.uuid, resp.ShortDebugString().c_str());
            }
            catch(...)
            {
                log_error("catch unknown exception, sence:%d, stype:%hu,svid:%u,uuid:%u,msg:%s", sence, data.svtype, data.svid, data.uuid, resp.ShortDebugString().c_str());
            }
        } while(false);
    }
};

template <int server, int cmd_req, typename Req>
class NotifyProxy {
public:
    bool Init()
    {
        return true;
    }
public:
    bool Handle(Req&& req, ListenSence sence)
    {
        log_debug("enter,sence:%d,req:%s", sence, req.ShortDebugString().c_str());

        return 0 == Ctx.host->MtSendtoSrv(cmd_req, server, &req, 0);
    }
};

